import java.io.*;
import java.lang.Float;
import java.lang.Long;
import java.util.*;
import javax.microedition.midlet.*;
import javax.microedition.lcdui.*;
import javax.microedition.rms.*;

class SimpleLongRecord {
    private long value;
    private String name;

    private final static int RECORDID = 1;

    SimpleLongRecord(String record_name, long default_value) {
	value = default_value;
	name = record_name;

	load();
    }

    public long get() {
	return value;
    }

    public void set(long val) {
	RecordStore db = null;
	try {
	    db = openRecordStore();
	    if (uninitialized(db)) {
		addRecord(db, val);
	    } else {
		updateRecord(db, val);
	    }
	} catch (Exception e) {
	} finally {
	    closeRecordStore(db);
	    value = val;
	}
    }

    private void load() {
	RecordStore db = null;
	try {
	    db = openRecordStore();
	    if (!uninitialized(db)) {
		value = readRecord(db);
	    }
	} catch (Exception e) {
	} finally {
	    closeRecordStore(db);
	}
    }

    private RecordStore openRecordStore() throws Exception {
	return RecordStore.openRecordStore(name, true);
    }

    private void closeRecordStore(RecordStore db) {
	try {
	    db.closeRecordStore();
	} catch (RecordStoreException e) {
	}
    }

    private boolean uninitialized(RecordStore db) throws Exception {
	return db.getNextRecordID() == RECORDID;
    }

    private long bytesToLong(byte[] bytes) throws Exception {
	ByteArrayInputStream bias = new ByteArrayInputStream(bytes);
	DataInputStream dis = new DataInputStream(bias);
	return dis.readLong();
    }

    private byte[] longToBytes(long val) throws Exception {
	ByteArrayOutputStream baos = new ByteArrayOutputStream();
	DataOutputStream dos = new DataOutputStream(baos);
	dos.writeLong(val);
	return baos.toByteArray();
    }

    private long readRecord(RecordStore db) throws Exception {
	return bytesToLong(db.getRecord(RECORDID));
    }

    private void addRecord(RecordStore db, long val) throws Exception {
	byte[] bytes = longToBytes(val);
	db.addRecord(bytes, 0, bytes.length);
	value = val;
    }

    private void updateRecord(RecordStore db, long val) throws Exception {
    	byte[] bytes = longToBytes(val);
	db.setRecord(RECORDID, bytes, 0, bytes.length);
	value = val;
    }
}

class SimpleDateRecord extends SimpleLongRecord {
    SimpleDateRecord(String record_name, long date) {
	super(record_name, date);
    }

    public void setDate(Date date) {
	set(date.getTime());
    }

    public Date getDate() {
	return new Date(get());
    }
}


public class CountMento extends MIDlet implements CommandListener {

    private SimpleDateRecord begin;
    private SimpleLongRecord reduced_days;
    private Date end_date;

    private DateField date_field;
    private TextField reduction_field;
    private StringItem progress;

    private Command exit_command, config_command, save_command, cancel_command;

    private Display display;
    private Form main;

    private Timer timer;
    private final static long UPDATE_INTERVAL = 1000;

    public CountMento() {
        display = Display.getDisplay(this);

        config_command = new Command("設定", Command.SCREEN, 10);
        exit_command = new Command("離開", Command.EXIT, 11);
        save_command = new Command("確定", Command.OK, 20);
        cancel_command = new Command("取消", Command.CANCEL, 21);

	progress = new StringItem("已完成:", "");
	progress.setFont(Font.getFont(Font.FACE_PROPORTIONAL, Font.STYLE_BOLD, Font.SIZE_LARGE));

	main = null;
	begin = new SimpleDateRecord("begin_date", 1220918400000L);
	reduced_days = new SimpleLongRecord("reduced_days", 0);
	end_date = endDate(begin.getDate(), reduced_days.get());
    }

    private long timeDiff(Date t1, Date t2) {
	return t1.getTime() - t2.getTime();
    }

    private long daysToMinisecords(long days) {
	return days * 24 * 60 * 60 * 1000L;
    }

    private Date endDate(Date begin_date, long reduced_days) {
	Calendar cal = Calendar.getInstance(TimeZone.getDefault());
	cal.setTime(begin_date);
	cal.set(Calendar.YEAR, cal.get(Calendar.YEAR) + 1);
	cal.setTime(new Date(cal.getTime().getTime() - daysToMinisecords(reduced_days)));
	return cal.getTime();
    }

    private float getPercentage() {
	Date now = Calendar.getInstance().getTime();
	float percent = 100f * timeDiff(now, begin.getDate()) / timeDiff(end_date, begin.getDate());

	if (percent < 0) {
	    return 0f;
	} else if (percent > 100) {
	    return 100f;
	} else {
	    return percent;
	}
    }


    private void switchToConfigView() {
	stopPeriodicalUpdate();

	Form config = new Form("設定日期");

	date_field = new DateField("入伍日期", DateField.DATE);
	date_field.setDate(begin.getDate());
	config.append(date_field);

	reduction_field = new TextField("折抵日期", "0", 3, TextField.NUMERIC);
	reduction_field.setString(Long.toString(reduced_days.get()));
	config.append(reduction_field);

        config.addCommand(save_command);
        config.addCommand(cancel_command);
        config.setCommandListener(this);

	display.setCurrent(config);
    }

    private void startPeriodicalUpdate() {
	timer = new Timer();
	timer.schedule(new TimerTask() {
	    public void run() {
		progress.setText(Float.toString(getPercentage()) + "%");
	    }
	}, 0, UPDATE_INTERVAL);
    }

    private void stopPeriodicalUpdate() {
	if (timer != null)
	    timer.cancel();
    }

    private String toYearMonthDay(Date date) {
	Calendar cal = Calendar.getInstance(TimeZone.getDefault());
	cal.setTime(date);
	return cal.get(Calendar.YEAR) + "年" +
	       (cal.get(Calendar.MONTH) + 1) + "月" +
	       cal.get(Calendar.DATE) + "日";
    }

    private long remainingDays() {
	Date now = Calendar.getInstance().getTime();
	long diff = end_date.getTime() - now.getTime();
	return diff / 86400000;
    }

    private void appendStringItem(String key, String value) {
	main.append(new StringItem(key, value));
    }

    private void switchToProgressView() {
	if (main != null) {
	    main.deleteAll();
	} else {
	    main = new Form("退伍倒數計時器");
	}
	appendStringItem("退伍日期:", toYearMonthDay(end_date) + "\n");
	long remain = remainingDays();
	if (remain > 0)
	    appendStringItem("剩下:", Long.toString(remain) + "天\n");

	progress.setText(Float.toString(getPercentage()) + "%");
	main.append(progress);

	if (remain <= 0) {
	    StringItem cong = new StringItem("恭喜退伍!", "");
	    cong.setFont(Font.getFont(Font.FACE_PROPORTIONAL, Font.STYLE_BOLD, Font.SIZE_LARGE));
	    main.append(cong);
	}

	main.addCommand(config_command);
	main.addCommand(exit_command);
        main.setCommandListener(this);

	display.setCurrent(main);

	if (remain > 0)
	    startPeriodicalUpdate();
    }

    public void startApp() {
	switchToProgressView();
    }

    private void saveConfig(Date date, long reduction) {
	begin.setDate(date);
    	reduced_days.set(reduction);
	end_date = endDate(begin.getDate(), reduced_days.get());
    }

    public void pauseApp() {
	stopPeriodicalUpdate();
    }

    public void destroyApp(boolean unconditional) {
	stopPeriodicalUpdate();
    }

    public void commandAction(Command cmd, Displayable s) {
        if (cmd == config_command) {
	    switchToConfigView();
	} else if (cmd == save_command) {
	    Date begin_date = date_field.getDate();
	    long reduction = Long.parseLong(reduction_field.getString());

	    saveConfig(begin_date, reduction);
	    switchToProgressView();
	} else if (cmd == cancel_command) {
	    switchToProgressView();
	} else if (cmd == exit_command) {
	    destroyApp(false);
	    notifyDestroyed();
        }
    }
}
